//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package roadmap_dao

import (
	"context"
	"regexp"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/roadmap_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type BasicRoadmapInfoDbItem struct {
	Title string `bson:"title"`
	Desc  string `bson:"desc"`
}

type RoadmapInfoDbItem struct {
	RoadmapId   string                 `bson:"_id"`
	BasicInfo   BasicRoadmapInfoDbItem `bson:"basicInfo"`
	OwnerType   uint32                 `bson:"ownerType"`
	PubState    bool                   `bson:"pubState"`
	TagList     []string               `bson:"tagList"`
	HotValue    uint32                 `bson:"hotValue"`
	OwnerUserId string                 `bson:"ownerUserId"`
	CreateTime  int64                  `bson:"createTime"`
	UpdateTime  int64                  `bson:"updateTime"`
}

func (item *RoadmapInfoDbItem) ToRoadmapInfo(userPerm *roadmap_api.UserPerm) *roadmap_api.RoadmapInfo {
	if item.TagList == nil {
		item.TagList = []string{}
	}
	return &roadmap_api.RoadmapInfo{
		RoadmapId: item.RoadmapId,
		BasicInfo: &roadmap_api.BasicRoadmapInfo{
			Title: item.BasicInfo.Title,
			Desc:  item.BasicInfo.Desc,
		},
		OwnerType:   roadmap_api.ROADMAP_OWNER_TYPE(item.OwnerType),
		PubState:    item.PubState,
		OwnerUserId: item.OwnerUserId,
		TagList:     item.TagList,
		CreateTime:  item.CreateTime,
		UpdateTime:  item.UpdateTime,
		HotValue:    item.HotValue,
		UserPerm:    userPerm,
	}
}

type _RoadmapInfoDao struct {
	mc *mongo.Collection
}

func newRoadmapInfoDao(db *mongo.Database) (*_RoadmapInfoDao, error) {
	mc := db.Collection(db_ns.DB_NS_ROADMAP + "info")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"ownerType", 1}, {"ownerUserId", 1}, {"updateTime", 1}},
		},
		{
			Keys: bson.D{{"tagList", 1}, {"updateTime", 1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_RoadmapInfoDao{mc: mc}, nil
}

func (db *_RoadmapInfoDao) Insert(ctx context.Context, dbItem *RoadmapInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_RoadmapInfoDao) Exist(ctx context.Context, roadmapId string, filterByPubState, pubState bool) (bool, error) {
	query := bson.M{"_id": roadmapId}
	if filterByPubState {
		query["pubState"] = pubState
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return count > 0, err
}

func (db *_RoadmapInfoDao) Get(ctx context.Context, roadmapId string) (*RoadmapInfoDbItem, error) {
	retItem := &RoadmapInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": roadmapId}).Decode(retItem)
	return retItem, err
}

func (db *_RoadmapInfoDao) Remove(ctx context.Context, roadmapId string) error {
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": roadmapId})
	return err
}

func (db *_RoadmapInfoDao) genListQuery(listParam *roadmap_api.ListParam, filterByPubState, pubState bool) bson.M {
	condList := []bson.M{}
	if listParam.FilterByOwner {
		condList = append(condList, bson.M{"ownerType": listParam.OwnerType})
	}
	if listParam.FilterByOwnerUserId {
		condList = append(condList, bson.M{"ownerUserId": listParam.OwnerUserId})
	}
	if listParam.FilterByKeyword {
		regex := primitive.Regex{
			Pattern: regexp.QuoteMeta(listParam.Keyword),
			Options: "i",
		}
		condList = append(condList, bson.M{"$or": []bson.M{
			{"basicInfo.title": regex},
			{"basicInfo.desc": regex},
		}})
	}
	if listParam.FilterByTag {
		condList = append(condList, bson.M{"tagList": listParam.Tag})
	}
	if filterByPubState {
		condList = append(condList, bson.M{"pubState": pubState})
	}
	if len(condList) == 0 {
		return bson.M{}
	}
	return bson.M{"$and": condList}
}

func (db *_RoadmapInfoDao) Count(ctx context.Context, listParam *roadmap_api.ListParam, filterByPubState, pubState bool) (uint32, error) {
	query := db.genListQuery(listParam, filterByPubState, pubState)
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_RoadmapInfoDao) List(ctx context.Context, listParam *roadmap_api.ListParam, filterByPubState, pubState bool, offset, limit int64) ([]*RoadmapInfoDbItem, error) {
	query := db.genListQuery(listParam, filterByPubState, pubState)
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"updateTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*RoadmapInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_RoadmapInfoDao) ListById(ctx context.Context, roadmapIdList []string) ([]*RoadmapInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": roadmapIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*RoadmapInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_RoadmapInfoDao) UpdateBasicInfo(ctx context.Context, roadmapId string, basicDbItem *BasicRoadmapInfoDbItem, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": roadmapId}, bson.M{"$set": bson.M{"basicInfo": basicDbItem, "updateTime": updateTime}})
	return err
}

func (db *_RoadmapInfoDao) UpdatePubState(ctx context.Context, roadmapId string, pubState bool) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": roadmapId}, bson.M{"$set": bson.M{"pubState": pubState}})
	return err
}

func (db *_RoadmapInfoDao) UpdateOwnerUser(ctx context.Context, roadmapId, ownerUserId string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": roadmapId}, bson.M{"$set": bson.M{"ownerUserId": ownerUserId}})
	return err
}

func (db *_RoadmapInfoDao) UpdateTagList(ctx context.Context, roadmapId string, tagList []string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": roadmapId}, bson.M{"$set": bson.M{"tagList": tagList}})
	return err
}

func (db *_RoadmapInfoDao) IncHotValue(ctx context.Context, roadmapId string, incValue uint32) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": roadmapId}, bson.M{"$inc": bson.M{"hotValue": incValue}})
	return err
}

func (db *_RoadmapInfoDao) DistinctRoadmapId(ctx context.Context, roadmapIdList []string) ([]string, error) {
	tmpItemList, err := db.mc.Distinct(ctx, "_id", bson.M{"_id": bson.M{"$in": roadmapIdList}})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpItem := range tmpItemList {
		retList = append(retList, tmpItem.(string))
	}
	return retList, nil
}
